#ifndef _PHMATRIX_H_
#define _PHMATRIX_H_

#include "PhVec3.h"

#pragma intrinsic(sin, cos)

// A class used to define a 4 by 4 floating point 
// matrix with common functionality
class PhMatrix
{
public:

	// Used to allow the data in the matrix to 
	// be accessed in different ways
	union
	{
		// The matrix as a 2d array
		float _m2D[4][4];

		// The matrix as a 1d array
		float _m1D[16];

		// The matrix as individual components
		// Think of them as axis'
		// row vs column major does not matter
		struct 
		{
			// The X axis
			float Xx, Xy, Xz, Xw;
			// The Y axis
			float Yx, Yy, Yz, Yw;
			// The Z axis
			float Zx, Zy, Zz, Zw;            
			// The W axis - Translation
			float Wx, Wy, Wz, Ww;        
		};

		struct
		{
			// The X axis
			PHVEC3 XAxis;            float padX;
			// The Y axis
			PHVEC3 YAxis;            float padY;
			// The Z axis
			PHVEC3 ZAxis;            float padZ;
			// The W axis - Translation
			PHVEC3 WAxis;            float padW;
		};
	};

	// Constructors
	PHMATRIX() {}
	PHMATRIX(   float xx, float xy, float xz, float xw,
		float yx, float yy, float yz, float yw,
		float zx, float zy, float zz, float zw,
		float wx, float wy, float wz, float ww);

	// These methods change the matrix into a specific type of matrix
	PHMATRIX &makeZero();        
	PHMATRIX &makeIdentity();        
	// Changes the matrix to be an X rotation matrix
	PHMATRIX &makeRotationX(float radians);
	// Changes the matrix to be an Y rotation matrix
	PHMATRIX &makeRotationY(float radians);
	// Changes the matrix to be an Z rotation matrix
	PHMATRIX &makeRotationZ(float radians);
	// Changes the matrix to be a scale matrix
	PHMATRIX &makeScale(float xScale, float yScale, float zScale);
	// Changes the matrix to be a perspective matrix
	PHMATRIX &makePerspective(float fFov, float fAspect, 
		float fNear, float fFar);

	// Matrix multiply method
	// out = a * b
	// The out cannot be the same as the a or b
	friend void Multiply(PHMATRIX &out, const PHMATRIX &a, const PHMATRIX &b);

	// Matrix multiply method, slower than friend func Multiply
	// temp = this;
	// this = temp * a
	//PHMATRIX &operator*=(const PHMATRIX &a);

	// Local rotation methods function as pre multiplies
	// this = this * makeRotation
	PHMATRIX &rotateLocalX(float radians);
	PHMATRIX &rotateLocalY(float radians);
	PHMATRIX &rotateLocalZ(float radians);

	// Global rotation methods function as post multiplies
	// this = makeRotation * this
	PHMATRIX &rotateGlobalX(float radians);
	PHMATRIX &rotateGlobalY(float radians);
	PHMATRIX &rotateGlobalZ(float radians);

	PHMATRIX &translateLocal(const float3 &tran)
	{
		return translateLocal(tran.x, tran.y, tran.z);
	}
	PHMATRIX &translateLocal(float x, float y, float z);

	PHMATRIX &translateGlobal(const float3 &tran)
	{
		return translateGlobal(tran.x, tran.y, tran.z);
	}
	PHMATRIX &translateGlobal(float x, float y, float z);

	// Scales the x, y and z axis' of the matrix
	PHMATRIX &scale(const float3 &scaleVec);
	// Scales the x, y and z axis' of the matrix
	PHMATRIX &scale(float x, float y, float z);

	// Transposes the matrix
	PHMATRIX &transpose(void);
	// Transposes the orientation of the matrix
	PHMATRIX &transpose3x3(void);

	friend void RotateVector(float3 &out, const float3 &inVec,
		const PHMATRIX &inMat);
	friend void TranslateVector(float3 &out, const float3 &inVec,
		const PHMATRIX &inMat);

	float3 getScale(void) const;

};

#endif